<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Nginx CORS配置指南 | 技术小馆</title>
    <link rel="stylesheet" href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css">
    <link rel="stylesheet" href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Noto Sans SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            line-height: 1.8;
            color: #333;
        }
        .hero {
            background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
        }
        .code-block {
            position: relative;
            background: #282c34;
            border-radius: 8px;
            overflow: hidden;
        }
        .code-header {
            background: #1e1e1e;
            color: #9d9d9d;
            padding: 8px 16px;
            font-size: 0.9rem;
            border-top-left-radius: 8px;
            border-top-right-radius: 8px;
        }
        pre {
            margin: 0;
            padding: 20px;
            overflow-x: auto;
        }
        code {
            color: #abb2bf;
            font-family: 'Courier New', monospace;
        }
        .card {
            transition: all 0.3s ease;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
        }
        .card:hover {
            transform: translateY(-5px);
            box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
        }
        .note {
            border-left: 4px solid #4299e1;
            background: #ebf8ff;
        }
        .warning {
            border-left: 4px solid #ecc94b;
            background: #fefcbf;
        }
        .section-title {
            position: relative;
            padding-left: 24px;
        }
        .section-title:before {
            content: "";
            position: absolute;
            left: 0;
            top: 50%;
            transform: translateY(-50%);
            width: 8px;
            height: 8px;
            background: #4299e1;
            border-radius: 50%;
        }
    </style>
</head>
<body class="bg-gray-50">
    <!-- Hero Section -->
    <section class="hero text-white py-20 px-4">
        <div class="container mx-auto max-w-5xl">
            <div class="flex flex-col items-center text-center">
                <div class="bg-white bg-opacity-10 p-3 rounded-full mb-6">
                    <i class="fas fa-server text-3xl"></i>
                </div>
                <h1 class="text-4xl md:text-5xl font-bold mb-4 font-serif">Nginx CORS 配置指南</h1>
                <p class="text-xl md:text-2xl text-blue-100 max-w-3xl mb-8">
                    跨域资源共享的安全实现与最佳实践
                </p>
                <div class="flex space-x-4">
                    <span class="px-4 py-2 bg-white bg-opacity-20 rounded-full text-sm">#Nginx</span>
                    <span class="px-4 py-2 bg-white bg-opacity-20 rounded-full text-sm">#Web安全</span>
                    <span class="px-4 py-2 bg-white bg-opacity-20 rounded-full text-sm">#后端开发</span>
                </div>
            </div>
        </div>
    </section>

    <!-- Main Content -->
    <main class="container mx-auto max-w-5xl px-4 py-12">
        <!-- Introduction -->
        <section class="mb-16">
            <div class="prose max-w-none">
                <h2 class="text-2xl font-bold mb-6">什么是 CORS？</h2>
                <p class="text-gray-700">
                    跨域资源共享 (CORS) 是一种机制，它通过允许服务器在响应中添加特定的 HTTP 头来控制哪些域可以访问资源。在 Nginx 中实现 CORS 涉及配置这些响应头以允许来自不同源的请求。
                </p>
                
                <div class="mt-8 note p-6 rounded-lg mb-8">
                    <div class="flex items-start">
                        <i class="fas fa-info-circle text-blue-500 text-xl mr-3 mt-1"></i>
                        <div>
                            <h3 class="font-bold text-lg mb-2">重要提示</h3>
                            <p>CORS 是浏览器强制执行的安全机制。即使服务器响应中包含 CORS 头，也不会降低服务器的安全性，因为这些头只是告诉浏览器哪些客户端可以访问响应。</p>
                        </div>
                    </div>
                </div>
                
                <div class="mt-8">
                    <h3 class="text-xl font-semibold mb-4">CORS 工作原理示意图</h3>
                    <div id="cors-diagram" class="mermaid">
                        graph TD
                            A[客户端] -->|发送跨域请求| B[服务器]
                            B -->|包含CORS头| A
                            A -->|检查CORS头| C{是否允许?}
                            C -->|是| D[加载资源]
                            C -->|否| E[阻止请求]
                    </div>
                </div>
            </div>
        </section>

        <!-- Config Section 1 -->
        <section class="mb-16">
            <h2 class="text-2xl font-bold mb-8 section-title">1. 基础 CORS 配置</h2>
            
            <div class="grid md:grid-cols-2 gap-8 mb-8">
                <div class="card bg-white p-6 rounded-lg">
                    <h3 class="text-xl font-semibold mb-4 text-blue-600">
                        <i class="fas fa-cog mr-2"></i> 基本配置
                    </h3>
                    <p class="text-gray-700 mb-4">
                        最简单的 CORS 配置是允许任何来源访问资源。这通过添加 <code>Access-Control-Allow-Origin: *</code> 头实现。
                    </p>
                    <div class="code-block mt-4">
                        <div class="code-header">
                            <i class="fas fa-code mr-2"></i>nginx.conf
                        </div>
                        <pre><code>location / {
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'Authorization, Content-Type, X-Requested-With';
    
    proxy_pass http://backend;
}</code></pre>
                    </div>
                </div>
                
                <div class="card bg-white p-6 rounded-lg">
                    <h3 class="text-xl font-semibold mb-4 text-blue-600">
                        <i class="fas fa-shield-alt mr-2"></i> 安全考虑
                    </h3>
                    <p class="text-gray-700 mb-4">
                        虽然通配符 (*) 很方便，但在生产环境中应该限制允许的源。这可以通过动态设置 <code>Access-Control-Allow-Origin</code> 头实现。
                    </p>
                    <div class="bg-blue-50 p-4 rounded-lg">
                        <h4 class="font-semibold mb-2">最佳实践：</h4>
                        <ul class="list-disc pl-5 space-y-1">
                            <li>始终指定允许的方法</li>
                            <li>限制允许的请求头</li>
                            <li>在生产环境中避免使用通配符</li>
                            <li>考虑添加 <code>Vary: Origin</code> 头</li>
                        </ul>
                    </div>
                </div>
            </div>
            
            <div class="warning p-6 rounded-lg">
                <div class="flex items-start">
                    <i class="fas fa-exclamation-triangle text-yellow-500 text-xl mr-3 mt-1"></i>
                    <div>
                        <h3 class="font-bold text-lg mb-2">警告</h3>
                        <p>当使用 <code>Access-Control-Allow-Credentials: true</code> 时，<code>Access-Control-Allow-Origin</code> 不能使用通配符 (*)，必须指定具体的来源。</p>
                    </div>
                </div>
            </div>
        </section>

        <!-- Config Section 2 -->
        <section class="mb-16">
            <h2 class="text-2xl font-bold mb-8 section-title">2. 预检请求处理</h2>
            
            <div class="grid md:grid-cols-2 gap-8 mb-8">
                <div class="card bg-white p-6 rounded-lg">
                    <h3 class="text-xl font-semibold mb-4 text-blue-600">
                        <i class="fas fa-exchange-alt mr-2"></i> 什么是预检请求？
                    </h3>
                    <p class="text-gray-700 mb-4">
                        对于某些跨域请求，浏览器会先发送一个 OPTIONS 请求（预检请求）来检查服务器是否允许实际请求。
                    </p>
                    <div class="bg-purple-50 p-4 rounded-lg">
                        <h4 class="font-semibold mb-2">触发预检请求的情况：</h4>
                        <ul class="list-disc pl-5 space-y-1">
                            <li>非简单请求方法（PUT、DELETE 等）</li>
                            <li>自定义请求头</li>
                            <li>Content-Type 不是 application/x-www-form-urlencoded、multipart/form-data 或 text/plain</li>
                        </ul>
                    </div>
                </div>
                
                <div class="card bg-white p-6 rounded-lg">
                    <h3 class="text-xl font-semibold mb-4 text-blue-600">
                        <i class="fas fa-code mr-2"></i> 预检请求配置
                    </h3>
                    <p class="text-gray-700 mb-4">
                        服务器需要正确响应 OPTIONS 请求并返回适当的 CORS 头。
                    </p>
                    <div class="code-block mt-4">
                        <div class="code-header">
                            <i class="fas fa-code mr-2"></i>nginx.conf
                        </div>
                        <pre><code>location / {
    if ($request_method = OPTIONS) {
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
        add_header Access-Control-Allow-Headers 'Authorization, Content-Type, X-Requested-With';
        return 204;
    }
    
    add_header Access-Control-Allow-Origin *;
    proxy_pass http://backend;
}</code></pre>
                    </div>
                </div>
            </div>
            
            <div class="prose max-w-none bg-white p-6 rounded-lg">
                <h3 class="text-xl font-semibold mb-4">预检请求流程图</h3>
                <div id="preflight-diagram" class="mermaid">
                    sequenceDiagram
                        participant C as 客户端
                        participant S as 服务器
                        C->>S: OPTIONS 请求 (预检)
                        S->>C: 204 No Content + CORS 头
                        C->>S: 实际请求 (GET/POST)
                        S->>C: 实际响应 + CORS 头
                </div>
            </div>
        </section>

        <!-- Config Section 3 -->
        <section class="mb-16">
            <h2 class="text-2xl font-bold mb-8 section-title">3. 动态源设置</h2>
            
            <div class="grid md:grid-cols-2 gap-8">
                <div class="card bg-white p-6 rounded-lg">
                    <h3 class="text-xl font-semibold mb-4 text-blue-600">
                        <i class="fas fa-random mr-2"></i> 动态源配置
                    </h3>
                    <p class="text-gray-700 mb-4">
                        使用 Nginx 的 <code>map</code> 指令可以动态设置 <code>Access-Control-Allow-Origin</code> 头，只允许特定的来源访问资源。
                    </p>
                    <div class="code-block mt-4">
                        <div class="code-header">
                            <i class="fas fa-code mr-2"></i>nginx.conf
                        </div>
                        <pre><code>http {
    map $http_origin $cors_origin {
        default "";
        "https://example.com" $http_origin;
        "https://another.com" $http_origin;
    }

    server {
        location / {
            add_header Access-Control-Allow-Origin $cors_origin;
            # 其他配置...
        }
    }
}</code></pre>
                    </div>
                </div>
                
                <div class="card bg-white p-6 rounded-lg">
                    <h3 class="text-xl font-semibold mb-4 text-blue-600">
                        <i class="fas fa-key mr-2"></i> 凭据与 CORS
                    </h3>
                    <p class="text-gray-700 mb-4">
                        当请求需要发送凭据（如 Cookies）时，需要额外的配置：
                    </p>
                    <div class="code-block mt-4">
                        <div class="code-header">
                            <i class="fas fa-code mr-2"></i>nginx.conf
                        </div>
                        <pre><code>location / {
    add_header Access-Control-Allow-Origin 'https://trusted.com';
    add_header Access-Control-Allow-Credentials true;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'Content-Type, Authorization';
    
    if ($request_method = OPTIONS) {
        return 204;
    }
}</code></pre>
                    </div>
                    <div class="mt-4 p-4 bg-red-50 rounded-lg">
                        <p class="text-sm text-red-700"><i class="fas fa-exclamation-circle mr-2"></i>注意：当使用 <code>Access-Control-Allow-Credentials: true</code> 时，<code>Access-Control-Allow-Origin</code> 不能使用通配符，必须指定明确的来源。</p>
                    </div>
                </div>
            </div>
        </section>

        <!-- Advanced Section -->
        <section class="mb-16">
            <h2 class="text-2xl font-bold mb-8 section-title">4. 高级配置与最佳实践</h2>
            
            <div class="grid md:grid-cols-3 gap-6 mb-8">
                <div class="card bg-white p-6 rounded-lg">
                    <div class="text-blue-500 text-3xl mb-4">
                        <i class="fas fa-clock"></i>
                    </div>
                    <h3 class="text-xl font-semibold mb-2">缓存控制</h3>
                    <p class="text-gray-700">
                        为预检请求结果设置缓存时间可以减少不必要的请求：
                        <code>Access-Control-Max-Age: 86400</code>
                    </p>
                </div>
                
                <div class="card bg-white p-6 rounded-lg">
                    <div class="text-blue-500 text-3xl mb-4">
                        <i class="fas fa-eye"></i>
                    </div>
                    <h3 class="text-xl font-semibold mb-2">暴露头信息</h3>
                    <p class="text-gray-700">
                        使用 <code>Access-Control-Expose-Headers</code> 让客户端可以访问特定的响应头。
                    </p>
                </div>
                
                <div class="card bg-white p-6 rounded-lg">
                    <div class="text-blue-500 text-3xl mb-4">
                        <i class="fas fa-redo"></i>
                    </div>
                    <h3 class="text-xl font-semibold mb-2">Vary 头</h3>
                    <p class="text-gray-700">
                        当动态设置 <code>Access-Control-Allow-Origin</code> 时，应该添加 <code>Vary: Origin</code> 头以避免缓存问题。
                    </p>
                </div>
            </div>
            
            <div class="bg-white p-6 rounded-lg">
                <h3 class="text-xl font-semibold mb-4">完整配置示例</h3>
                <div class="code-block">
                    <div class="code-header">
                        <i class="fas fa-code mr-2"></i>nginx.conf
                    </div>
                    <pre><code>http {
    map $http_origin $cors_origin {
        default "";
        "~^https://(www\.)?example\.com$" $http_origin;
        "~^https://(.*\.)?trusted\.com$" $http_origin;
    }

    server {
        listen 80;
        server_name api.example.com;

        location / {
            if ($request_method = OPTIONS) {
                add_header Access-Control-Allow-Origin $cors_origin;
                add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
                add_header Access-Control-Allow-Headers 'Authorization, Content-Type, X-Requested-With';
                add_header Access-Control-Allow-Credentials true;
                add_header Access-Control-Max-Age 86400;
                add_header Content-Length 0;
                add_header Content-Type text/plain;
                return 204;
            }

            add_header Access-Control-Allow-Origin $cors_origin;
            add_header Access-Control-Allow-Credentials true;
            add_header Access-Control-Expose-Headers 'X-Custom-Header';
            add_header Vary Origin;

            proxy_pass http://backend;
        }
    }
}</code></pre>
                </div>
            </div>
        </section>

        <!-- Summary -->
        <section class="bg-white p-8 rounded-lg shadow-sm">
            <h2 class="text-2xl font-bold mb-6 text-center">CORS 配置要点总结</h2>
            
            <div class="grid md:grid-cols-2 gap-6">
                <div>
                    <h3 class="text-xl font-semibold mb-3 text-blue-600">必选头信息</h3>
                    <ul class="space-y-2">
                        <li class="flex items-start">
                            <span class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-3 flex-shrink-0">1</span>
                            <span><code>Access-Control-Allow-Origin</code> - 控制哪些域可以访问资源</span>
                        </li>
                        <li class="flex items-start">
                            <span class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-3 flex-shrink-0">2</span>
                            <span><code>Access-Control-Allow-Methods</code> - 允许的HTTP方法</span>
                        </li>
                        <li class="flex items-start">
                            <span class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-3 flex-shrink-0">3</span>
                            <span><code>Access-Control-Allow-Headers</code> - 允许的请求头</span>
                        </li>
                    </ul>
                </div>
                
                <div>
                    <h3 class="text-xl font-semibold mb-3 text-blue-600">可选头信息</h3>
                    <ul class="space-y-2">
                        <li class="flex items-start">
                            <span class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-3 flex-shrink-0">1</span>
                            <span><code>Access-Control-Allow-Credentials</code> - 是否允许发送凭据</span>
                        </li>
                        <li class="flex items-start">
                            <span class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-3 flex-shrink-0">2</span>
                            <span><code>Access-Control-Expose-Headers</code> - 暴露给客户端的响应头</span>
                        </li>
                        <li class="flex items-start">
                            <span class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-3 flex-shrink-0">3</span>
                            <span><code>Access-Control-Max-Age</code> - 预检请求缓存时间</span>
                        </li>
                    </ul>
                </div>
            </div>
            
            <div class="mt-8 p-6 bg-blue-50 rounded-lg">
                <h3 class="text-xl font-semibold mb-3">调试技巧</h3>
                <div class="grid md:grid-cols-2 gap-4">
                    <div>
                        <p class="font-medium">浏览器开发者工具:</p>
                        <ul class="list-disc pl-5 mt-2 space-y-1">
                            <li>检查Network选项卡中的请求和响应头</li>
                            <li>查看控制台中的CORS错误信息</li>
                        </ul>
                    </div>
                    <div>
                        <p class="font-medium">命令行工具:</p>
                        <ul class="list-disc pl-5 mt-2 space-y-1">
                            <li>使用curl测试OPTIONS请求</li>
                            <li>检查响应头是否符合预期</li>
                        </ul>
                    </div>
                </div>
            </div>
        </section>
    </main>

    <!-- Footer -->
    <footer class="bg-gray-900 text-white py-12">
        <div class="container mx-auto max-w-5xl px-4">
            <div class="flex flex-col items-center">
                <h3 class="text-xl font-bold mb-2">技术小馆</h3>
                <p class="text-gray-400 mb-6">专业的Web技术资源中心</p>
                <a href="http://www.yuque.com/jtostring" class="text-blue-400 hover:text-blue-300 transition-colors duration-200">
                    <i class="fas fa-external-link-alt mr-2"></i>
                    http://www.yuque.com/jtostring
                </a>
            </div>
        </div>
    </footer>

    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <script>
        mermaid.initialize({
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis'
            },
            sequenceDiagram: {
                useMaxWidth: true,
                height: 300
            }
        });
        
        // 微交互效果
        document.querySelectorAll('a').forEach(link => {
            link.addEventListener('mouseenter', function() {
                this.style.transition = 'all 0.2s ease';
                this.style.opacity = '0.8';
            });
            link.addEventListener('mouseleave', function() {
                this.style.opacity = '1';
            });
        });
    </script>
</body>
</html>